﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Drawing;
using Common;

namespace ImageSpaceFiltersPlugin
{
    public class Histogram
    {
        protected int[] dataR = new int[256];
        protected int[] dataG = new int[256];
        protected int[] dataB = new int[256];
        protected int maxR=0, maxG=0, maxB=0;

        protected int[] mapR = new int[2256], mapG = new int[256], mapB = new int[256];

        void Init()
        {
            for (int i = 0; i < 256; i++)
            {
                dataR[i] = dataG[i] = dataB[i] = 0;
            }
        }

        public Histogram()
        {
            Init();
        }

        public Histogram(Bitmap bmp)
        {
            Init();
            ImageOperation.ImageSingleTransform(bmp, (ref float r, ref float g, ref float b, int x, int y) =>
                {
                    RegisterColor(Color.FromArgb((int)r, (int)g, (int)b));
                });
        }

        public enum HistogramChannel
        {
            R,G,B
        }
        public Bitmap DrawHistogram(int width, int height, Color c, HistogramChannel channel)
        {
            switch (channel)
            {
                case HistogramChannel.R:
                    return DrawHistogramInternal(width, height, c, dataR, maxR);
                case HistogramChannel.G:
                    return DrawHistogramInternal(width, height, c, dataG, maxG);
                case HistogramChannel.B:
                    return DrawHistogramInternal(width, height, c, dataB, maxB);
                default:
                    return null;
            }
        }

        private Bitmap DrawHistogramInternal(int width, int height, Color c, int[] data, int max)
        {
            Bitmap rs = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(rs);
            for (int i = 0; i < 256; i++)
            {
                float x1 = i / 256.0f *width;
                float x2 = (i + 1) / 256.0f*width;
                float y = data[i] / (float)max * (float)height;
                g.FillRectangle(new SolidBrush(c), new RectangleF(x1, height - y, x2 - x1, y)); 
            }
            return rs;
        }

        public void RegisterColor(Color c)
        {
            dataR[c.R]++;
            dataG[c.G]++;
            dataB[c.B]++;
            if (dataR[c.R] > maxR)
                maxR = dataR[c.R];
            if (dataG[c.G] > maxG)
                maxG = dataG[c.G];
            if (dataB[c.B] > maxB)
                maxB = dataB[c.B];
        }

        public int GetMaxR()
        {
            return maxR;
        }

        public int GetMaxG()
        {
            return maxG;
        }

        public int GetMaxB()
        {
            return maxB;
        }

        public int GetValuesR(byte x)
        {
            return dataR[x];
        }

        public int GetValuesG(byte x)
        {
            return dataG[x];
        }

        public int GetValuesB(byte x)
        {
            return dataB[x];
        }

        public void ComputeEqualizationMap(int w, int h)
        {
            int n = w * h;
            ComputeChannel(n, dataR, mapR);
            ComputeChannel(n, dataG, mapG);
            ComputeChannel(n, dataB, mapB);
        }

        public Color MapColor(Color cin)
        {
            return Color.FromArgb(mapR[cin.R], mapG[cin.G], mapB[cin.B]);
        }

        private void ComputeChannel(int n, int[] histogram, int[] map)
        {
            int minCdf = 0;
            for (int i = 0; i < 256; i++)
                if (histogram[i] != 0)
                {
                    minCdf = histogram[i];
                    break;
                }
            int cdf = 0;
            for (int i = 0; i < 256; i++)
            {
                cdf += histogram[i];
                map[i] = (int)Math.Round((cdf - minCdf) / (float)(n - minCdf) * 255.0f);
            }
        }
    }
}
